package com.panopset.app;

import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JPanel;

import org.joda.time.DateTime;

import com.panopset.Alert;
import com.panopset.Util;
import com.panopset.compat.RezIO;
import com.panopset.compat.Strings;
import com.panopset.sf.ThreadButton;
import com.panopset.sf.TitledFileSelection;
import com.panopset.sf.TitledTextArea;
import com.panopset.sf.Windoc;
import com.panopset.sf.apps.PanopsetApp;

/**
 * Will be ready for prime time with the release of Java 7.
 *
 * @author Karl Dinwiddie
 */
public class Discan extends PanopsetApp {

    @Override
    public JPanel createPanel(final Windoc w) {
        return new Doc(w).getPanel();
    }

    /**
     * @param args
     *            Not used.
     */
    public static void main(final String... args) {
        new Discan().show();
    }

    static class Result implements Comparable<Result> {
        final File f;
        Long size = 0L;
        boolean skipped = false;

        @Override
        public int hashCode() {
            return f.hashCode();
        }

        @Override
        public int compareTo(final Result o) {
            return o.size.compareTo(this.size);
        }

        @Override
        public boolean equals(final Object o) {
            if (o == null) {
                return false;
            }
            return ((Result) o).size.equals(this.size);
        }

        /**
         * @param file
         *            File.
         */
        public Result(final File file) {
            this.f = file;
        }
    }

    private class Doc {
        private final Windoc w;

        public Doc(final Windoc windoc) {
            w = windoc;
        }

        private final List<Result> results = new ArrayList<Result>();
        private long total = 0L;
        private boolean skipRequested;

        private void skip() {
            skipRequested = true;
        }

        private long accum(Result result, File dir) {
            if (skipRequested || dir == null) {
                result.skipped = true;
                return 0;
            }
            if (getTb().isHaltRequested()) {
                return 0L;
            }
            long rtn = 0;
            File[] files = dir.listFiles();
            if (files == null) {
                return 0;
            }
            for (File f : files) {
                if (!RezIO.getCanonicalPath(f).equals(f.getAbsolutePath())) {
                    continue;
                }
                if (new DateTime().getMillis() - lastUpdate > 2000) {
                    Alert.green("Scanning " + RezIO.getCanonicalPath(f));
                    update();
                }
                if (f.isDirectory()) {
                    if (f.getName().equals(dir.getName())) {
                        rtn = rtn + accum(result, f);
                    } else {
                        rtn = rtn + accum(result, f);
                    }
                } else {
                    rtn = rtn + f.length();
                }
            }
            result.size = result.size + rtn;
            return rtn;
        }

        private long totalSpace = 0L;

        private void scan(File dir) {
            totalSpace = dir.getTotalSpace();
            results.clear();
            total = 0L;
            for (File f : dir.listFiles()) {
                try {
                    f.getCanonicalPath();
                } catch (IOException ex) {
                    Alert.yellow("Skipping " + f.getName() + ":"
                            + ex.getMessage());
                    continue;
                }
                if (getTb().isHaltRequested()) {
                    return;
                }
                Result result = new Result(f);
                results.add(result);
                if (f.isDirectory()) {
                    result.size = accum(result, f);
                } else {
                    result.size = f.length();
                }
                total = total + result.size;
                if (skipRequested) {
                    skipRequested = false;
                }
            }
            update();
        }

        private long lastUpdate = 0L;

        private void update() {
            lastUpdate = new DateTime().getMillis();
            getTa().setText("");
            String lengthString = "";
            StringWriter sw = null;

            lengthString = Util.pad("" + format(total), ' ', 26, true);
            sw = new StringWriter();
            sw.append(lengthString);
            sw.append(" ");
            sw.append("*** SCANNED TOTAL ***");
            sw.append(Strings.getEol());
            getTa().append(sw.toString());

            Collections.sort(results);
            for (Result result : results) {
                lengthString = Util
                        .pad("" + format(result.size), ' ', 26, true);
                sw = new StringWriter();
                sw.append(lengthString);
                sw.append(" ");
                if (result.skipped) {
                    sw.append("**SKIPPED** ");
                }
                sw.append(result.f.getName());
                sw.append(Strings.getEol());
                getTa().append(sw.toString());
            }
        }

        private String format(long l) {
            DecimalFormat formatter = new DecimalFormat("#,###,###");
            return formatter.format(l);
        }

        private void scan() {
            getTa().setText("");
            File f = getPathTF().getFile();
            if (f == null) {
                Alert.red("File no specified.");
                return;
            }
            String cp = RezIO.getCanonicalPath(f);
            if (!f.exists()) {
                Alert.red("File does not exist:" + cp);
                return;
            }
            if (!f.isDirectory()) {
                Alert.red("File is not a directory:" + cp);
                return;
            }
            scan(f);
            Alert.clearYellow();
            Alert.green("Scan complete.");
        }

        private ThreadButton tb;

        private ThreadButton getTb() {
            if (tb == null) {
                tb = new ThreadButton() {

                    @Override
                    public void run() {
                        scan();
                    }

                    @Override
                    public String createText() {
                        return "Go";
                    }

                    @Override
                    public String createTipText() {
                        return "Scan selected disk";
                    }

                    @Override
                    public char createMnemonic() {
                        return KeyEvent.VK_G;
                    }

                    @Override
                    public String createThreadName() {
                        return "Scanner";
                    }

                };
            }
            return tb;
        }

        public JPanel getPanel() {
            JPanel p = w.cf.createPanel(new BorderLayout());
            JPanel np = w.cf.createPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.weightx = 1.0;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.gridx = 0;
            gbc.gridy = 0;
            np.add(getPathTF().getPanel(), gbc);
            gbc.gridx = gbc.gridx + 1;
            gbc.weightx = 0.0;
            np.add(getTb().getGui(), gbc);
            JButton skipBtn = new JButton(Util.x("Skip"));
            skipBtn.setToolTipText(Util.x("Skip scan of current directory"));
            skipBtn.setMnemonic(KeyEvent.VK_P);
            skipBtn.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    skip();
                }

            });
            w.cf.registerPassiveComponent(skipBtn);
            JPanel cp = w.cf.createPanel(new GridLayout(1, 1));
            cp.add(getTa().getPanel());
            p.add(BorderLayout.NORTH, np);
            p.add(BorderLayout.CENTER, cp);
            return p;
        }

        private TitledTextArea ta;

        private TitledTextArea getTa() {
            if (ta == null) {
                ta = w.cf.createTitledTextArea(w, Util.x("Report"),
                        Util.x("Scan report"), "report");
            }
            return ta;
        }

        private TitledFileSelection pathTF;

        private TitledFileSelection getPathTF() {
            if (pathTF == null) {
                pathTF = new TitledFileSelection(w, Util.x("Directory"),
                        Util.x("Directory to scan."),
                        JFileChooser.DIRECTORIES_ONLY,
                        JFileChooser.OPEN_DIALOG, "directory");
                if (!pathTF.getFile().exists()) {
                    pathTF.setFile(new File(System.getProperty("user.home")));
                }
            }
            return pathTF;
        }
    }
}
